package apibasej.basic.misc;

import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;

import apibasej.basic.exception.APIBaseException;
import apibasej.basic.misc.standalone.UtilPrimitives;

public class UtilStrObj {

	
	public static void main(String[] args) throws IllegalArgumentException, SecurityException, InstantiationException, IllegalAccessException, InvocationTargetException, NoSuchMethodException {
		System.out.println( Double.class.getConstructor(String.class).newInstance("0") );
		/*
		Timestamp t = new Timestamp(0);
		//Date d = t; // OK
		//t = d; // err
		System.out.println( (t instanceof Date) );
		System.out.println(Date.class.isAssignableFrom(Timestamp.class));
		System.out.println(Timestamp.class.isAssignableFrom(Date.class));
		
		for(Constructor<?> c : Timestamp.class.getConstructors()){
			System.out.println("c>"+c);
			for(Class<?> cl : c.getParameterTypes()){
				System.out.println("     >"+cl);
			}
		}
		*/
	}
	
	private static final String PATTERN_DATETIME_DEF = "yyyy-MM-dd HH:mm:ss.SSS Z"; // colocar Z para o timezone, para não ter proble se tiver diferença entre o timezone do server e do client
	
	public static String toStr(Object obj){
		return toStr(obj,PATTERN_DATETIME_DEF);
	}
	
	//TODO ver conversão para hex ou base64 se for java.sql.Blob ou byte[], etc ...
	
	public static String toStr(Object obj, String patternDateTime){
		if(obj==null) return null;
		
		if(obj instanceof Date){// ver gregorian calendar ...
			// formatar para retornar a string dos milis???
			return new SimpleDateFormat(patternDateTime).format((Date)obj);
		}else if(obj instanceof Class){
			return ((Class<?>)obj).getName();
		}else if(obj instanceof byte[]){
			return UtilCodec.encodeBase64((byte[])obj);
		}else{
			return obj.toString();
		}
	}
	
	public static <T> T toObj(String str, Class<T> typeObj) throws IllegalArgumentException, SecurityException, InstantiationException, IllegalAccessException, InvocationTargetException, NoSuchMethodException, ParseException, ClassNotFoundException, APIBaseException, IOException{
		return toObj(str, typeObj, false, PATTERN_DATETIME_DEF);
	}
	public static <T> T toObj(String str, Class<T> typeObj, boolean returnDefaultPrimitiveForNull) throws IllegalArgumentException, SecurityException, InstantiationException, IllegalAccessException, InvocationTargetException, NoSuchMethodException, ParseException, ClassNotFoundException, APIBaseException, IOException{
		return toObj(str, typeObj, returnDefaultPrimitiveForNull, PATTERN_DATETIME_DEF);
	}
	@SuppressWarnings("unchecked")
	public static <T> T toObj(String str, Class<T> typeObj, boolean returnDefaultPrimitiveForNull, String patternDateTime) throws IllegalArgumentException, SecurityException, InstantiationException, IllegalAccessException, InvocationTargetException, ParseException, ClassNotFoundException, NoSuchMethodException, APIBaseException, IOException{
		
		T resp;
		if(str==null) {
			resp = null;
		}else if(Date.class.isAssignableFrom(typeObj)){
			// ver gregorian calendar ...
			// formatar para retornar a string dos milis???
			//se usar milis ficará ilegível
			//ConfigManager.configDefaultsTimeZoneAndLocale();
			//resp = (T)typeObj.getDeclaredConstructor(long.class).newInstance(Long.parseLong(str));
			
			String p = patternDateTime;
			if(str.length()<p.length()) p = p.substring(0,str.length());
			Date d = new SimpleDateFormat(p).parse(str);
			
			resp = (T)typeObj.getDeclaredConstructor(long.class).newInstance(d.getTime());
		}else if(Class.class.isAssignableFrom(typeObj)){
			resp = (T)Class.forName(str);
		}else if(Character.class.isAssignableFrom(typeObj)){// precisa, pois Character não tem construtor recebendo string
			resp = (T)new Character(str.charAt(0));
		}else if(byte[].class.isAssignableFrom(typeObj)){
			resp = (T)UtilCodec.decodeBase64(str);
		}else{
			if(typeObj.isPrimitive()) {
				typeObj = UtilPrimitives.wrap(typeObj); // pois tipos primitivos não tem contrutores
			}
			try{
				resp = (T)typeObj.getDeclaredConstructor(String.class).newInstance(str);
			}catch (NoSuchMethodException e) {
				
				//TODO ver se tem método valueOf(String s) na classe !!! ????
				
				if(Character.class.equals(typeObj) && str.length()==1){
					resp = (T)new Character(str.charAt(0));
				}else{
					throw new APIBaseException("Constructor with String not found for: "+typeObj.getName(), e);
				}
			}
		}
		if(resp==null && returnDefaultPrimitiveForNull){
			resp = getDefaultForPrimitives(resp, typeObj);
		}
		return resp;
	}
	
	
	@SuppressWarnings("unchecked")
	public static <T> T getDefaultForPrimitives(Object v, Class<T> typeReturn) throws IllegalArgumentException, SecurityException, InstantiationException, IllegalAccessException, InvocationTargetException, NoSuchMethodException{
		if(v==null){
			if(Number.class.isAssignableFrom(typeReturn)) {
				//ver como fazer o cast para retornar o tipo certo (Double.class.cast(0) dá erro!!!)
				//ver melhor forma ... testar!!!!
				if(typeReturn.isPrimitive()) typeReturn = UtilPrimitives.wrap(typeReturn); // pois tipos primitivos não tem contrutores
				v = (T)typeReturn.getConstructor(String.class).newInstance("0"); 
			}else if(Boolean.class.isAssignableFrom(typeReturn)) {
				v = false;
			}
		}
		return (T)v;
	}
	
}
